/*
* 初始化一个命名Uniforms块中的uniform变量
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "../common/LoadShaders.h"

#define BUFFER_OFFSET(offset) ((void*) (offset))

const unsigned int SCR_WIDTH = 512;
const unsigned int SCR_HEIGHT = 512;

void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    {
        //将窗口设置为关闭，跳出循环
        glfwSetWindowShouldClose(window, 1);
    }
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}


int TypeSize(GLenum type)
{
    int size;

#define CASE(Enum, Count, Type) \
    case Enum: size=Count*sizeof(Type);break;


    switch (type) {
    CASE(GL_FLOAT,1,GLfloat);
    CASE(GL_FLOAT_VEC2,2,GLfloat);
    CASE(GL_FLOAT_VEC3,3,GLfloat);
    CASE(GL_FLOAT_VEC4,4,GLfloat);
    CASE(GL_INT,1,GLint);
    CASE(GL_INT_VEC2,2,GLint);
    CASE(GL_INT_VEC3,3,GLint);
    CASE(GL_INT_VEC4,4,GLint);
    CASE(GL_UNSIGNED_INT,1,GLuint);
    CASE(GL_UNSIGNED_INT_VEC2,2,GLuint);
    CASE(GL_UNSIGNED_INT_VEC3,3,GLuint);
    CASE(GL_UNSIGNED_INT_VEC4,4,GLuint);
    CASE(GL_BOOL,1,GLboolean);
    CASE(GL_BOOL_VEC2,2,GLboolean);
    CASE(GL_BOOL_VEC3,3,GLboolean);
    CASE(GL_BOOL_VEC4,4,GLboolean);
    CASE(GL_FLOAT_MAT2,4,GLfloat);
    CASE(GL_FLOAT_MAT2x3,6,GLfloat);
    CASE(GL_FLOAT_MAT2x4,8,GLfloat);
    CASE(GL_FLOAT_MAT3,9,GLfloat);
    CASE(GL_FLOAT_MAT3x2,6,GLfloat);
    CASE(GL_FLOAT_MAT3x4,12,GLfloat);
    CASE(GL_FLOAT_MAT4,16,GLfloat);
    CASE(GL_FLOAT_MAT4x2,8,GLfloat);
    CASE(GL_FLOAT_MAT4x3,12,GLfloat);
#undef CASE

    default:
        std::cout << stderr << "Unkown type: " << type << "\n";
        exit(EXIT_FAILURE);
        break;
    }
    return size;
}


enum VAO_IDs { Triangles, NumVAOs };
enum Buffer_IDs { ArrayBuffer, NumBuffers };
enum Attrib_IDs { vPosition = 0, vColor=1 };

GLuint VAOs[NumVAOs];
GLuint Buffers[NumBuffers];

const GLuint NumVertices = 6;

void init(void)
{
    glClearColor(1, 0, 0, 1);
    glGenVertexArrays(NumVAOs, VAOs);
    glBindVertexArray(VAOs[Triangles]);

    GLfloat vertices[NumVertices][2] = {
        { -0.90, -0.90 },
        {  0.85, -0.90 },
        { -0.90,  0.85 },
        {  0.90, -0.85 },
        {  0.90,  0.90 },
        { -0.85,  0.90 },
    };

    ShaderInfo shaders[] = {
        { GL_VERTEX_SHADER, "triangles.vert" },
        { GL_FRAGMENT_SHADER, "triangles.frag" },
        { GL_NONE, NULL }
    };

    GLuint program = LoadShaders(shaders);
    glUseProgram(program);

    /*初始化 uniforms*/
    GLuint uboIndex;
    GLint uboSize;
    GLuint ubo;
    GLvoid *buffer;

    uboIndex = glGetUniformBlockIndex(program, "Uniforms");

    glGetActiveUniformBlockiv(program, uboIndex,
                              GL_UNIFORM_BLOCK_DATA_SIZE, &uboSize);

    buffer = malloc(uboSize);

    if (buffer == NULL)
    {
        std::cout << stderr << "Unable to allocate buffer\n";
        exit(EXIT_FAILURE);
    }
    else{
        enum { Translation, Scale, Rotation, Enabled, NumUniforms };

        /*准备数据*/
        GLfloat scale = 0.5;
        GLfloat translation[] = { 0.3, 0.0, 0.0 };
//        GLfloat translation[] = {1, 0, 0 };
        GLfloat rotation[] = { 45, 0.0, 0.0, 1.0 };
        GLboolean enabled = GL_TRUE;

        /*建立变量名数组，对应uniform变量*/
        const char* names[NumUniforms] = {
            "translation", "scale", "rotation", "enabled"
        };

        /*查询对应的属性，以便向缓存写入数据*/
        GLuint indices[NumUniforms]; // 变量索引位置
        GLint size[NumUniforms];
        GLint offset[NumUniforms];
        GLint type[NumUniforms];

        glGetUniformIndices(program, NumUniforms, names, indices);

        glGetActiveUniformsiv(program, NumUniforms, indices,
                              GL_UNIFORM_OFFSET, offset);
        glGetActiveUniformsiv(program, NumUniforms, indices,
                              GL_UNIFORM_SIZE, size);
        glGetActiveUniformsiv(program, NumUniforms, indices,
                              GL_UNIFORM_TYPE, type);
        /*把uniform变量值拷贝到缓存中*/
        memcpy((char*)buffer+offset[Scale], &scale,
               size[Scale]*TypeSize(type[Scale]));
        memcpy((char*)buffer+offset[Translation], &translation,
               size[Translation]*TypeSize(type[Translation]));
        memcpy((char*)buffer+offset[Rotation], &rotation,
               size[Rotation]*TypeSize(type[Rotation]));
        memcpy((char*)buffer+offset[Enabled], &enabled,
               size[Enabled]*TypeSize(type[Enabled]));
    }

    glGenBuffers(1, &ubo);
    glBindBuffer(GL_UNIFORM_BUFFER, ubo);
    glBufferData(GL_UNIFORM_BUFFER, uboSize,
                 buffer, GL_STATIC_DRAW);
    glBindBufferBase(GL_UNIFORM_BUFFER, uboIndex, ubo);
    //将缓冲区对象缓冲区绑定到target指定的目标数组的索引索引处的绑定点

    // vpos
    glGenBuffers(NumBuffers, Buffers);
    glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices),
                 vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(vPosition, 2, GL_FLOAT,
                          GL_FALSE, 0, BUFFER_OFFSET(0));


    //    glVertexAttribPointer(vColor, 4, GL_UNSIGNED_BYTE,
    //                          GL_TRUE, sizeof(VertexData),
    //                          BUFFER_OFFSET(0));

    //    glEnableVertexAttribArray(vColor);
    glEnableVertexAttribArray(vPosition);
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    glBindVertexArray(VAOs[Triangles]);
    glDrawArrays(GL_TRIANGLES, 0, NumVertices);
    glFlush();
}


int main()
{
    //glfw初始化
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    //glfw创建窗口
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "OpenGL编程指南", NULL, NULL);

    if (window == NULL)
    {
        printf("创建窗口失败");
        //终止
        glfwTerminate();
        return -1;
    }

    //显示窗口
    glfwMakeContextCurrent(window);

    //设置回调，当窗口大小调整后将调用该回调函数
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // glad初始化
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        printf("加载失败");
        return -1;
    }

    init();

    // 使用循环达到循环渲染效果
    while (!glfwWindowShouldClose(window))
    {
        display();
        //自定义输入事件
        processInput(window);
        //交互缓冲区，否则显示空白
        glfwSwapBuffers(window);
        //输入输出事件,否则无法对窗口进行交互
        glfwPollEvents();
    }

    //终止渲染 关闭并清理glfw本地资源
    glfwTerminate();
    return 0;
}
